/* Copyright 2001,2002,2003 NAH6
 * All Rights Reserved
 *
 * Parts Copyright DoD, Parts Copyright Starium
 *
 */
/*LINTLIBRARY*/
/*PROTOLIB1*/

/*#include <math.h>*/
#include <string.h>
#include "main.h"
#include "delay.h"
#include "con_adap.h"

#define USE_LONG_INTERP_WINDOWS  TRUE


/*************************************************************************
*
* ROUTINE
*               ConstructAdaptCW
*
* FUNCTION
*               Adaptive VQ (n-taps, where n = 0, ..., 3)
*               "self-excited" or "vq" or "adaptive code book"
*
* SYNOPSIS
*               ConstructAdaptCW(residual, ResLen, AdaptCB, AdaptCBLen,
*                       AdaptGain, AdaptDelay, Type)
*   formal
*
*                       data    I/O
*       name            type    type    function
*       -------------------------------------------------------------------
*       residual        fxpt_16 i/o     Data segement/Filtered data segment
*       ResLen          int      i      Dimension of residual
*       AdaptCB         fxpt_16  i      Adaptive Codebook
*       AdaptCBLen      int      i      Dimension of Adaptive codebook
*       AdaptDelay      fxpt_16  i      Adaptive codebook delay
*       AdaptGain       fxpt_16  i      Adaptive codebook gain
*       Type            char     i      Type 'long' calls ldelay.f, the
*                                       delay routine using long
*                                       interpolation windows.  Type 'short'
*                                       calls delay.f, the short delay
*                                       routine.
*==========================================================================
*
* USAGE
*
*    Adaptive code book (pitch) synthesis routine:
*
*    1) For lags < frame size:  gain-shape adaptive code book VQ
*
*    2) For lags => frame size this is equivalent to a pitch synthesis "filter"
*
*                              -[b(1)-1]        -b(1)        -[b(1)+1]
*       H(z) =  1 /  1 + b(2) z         + b(3) z     + b(4) z
*
*==========================================================================
*
* REFERENCES
*
*       Singhal & Atal, Improving Performance of Multi-Pulse LPC Coders at
*       Low Bit Rates, ICASSP, 1984.
*
*       Rose & Barnwell.  The Self Excited Vocoder-An Alternate Approach
*       to Toll Quality at 4800 bps, ICASSP, 1986, pp. 453-456.
*
*       Kleijn, Krasinski and Ketchum, Improved Speech Quality and
*       Efficient Vector Quantization in SELP, ICASSP, 1988.
*
***************************************************************************/
void ConstructAdaptCW(
fxpt_16 residual[RES_LEN],      /* 15.0 format */
fxpt_16 AdaptCB[MAX_ABUF_LEN],  /* 15.0 format */
fxpt_16 AdaptGain,              /* 1.14 format */
fxpt_16 AdaptDelay              /* 8.7 format */
)
{
        int     m;
        fxpt_16 frac;           /* 8.7 format */
        int     i;
        fxpt_16 buf[MAX_A_LEN];
		fxpt_16 *pcb1,*pcb2;

FXPT_PUSHSTATE("ConstructAdaptCW", -1.0, -1.0);
        /*  Initialize variables */
        m = fxpt_shr16_fast(AdaptDelay, 7);
        frac = fxpt_sub16(AdaptDelay, fxpt_shl16_fast(m, 7));

        /*  Update codebook */
        for(i= ACB_SIZE - SF_LEN, pcb1= AdaptCB,pcb2=AdaptCB+SF_LEN; i>0; --i,++pcb1,++pcb2)
                *pcb1= *pcb2;

        /*  Update codebook with selected memory from selected delay (m) */
        if (frac == 0) {
                for (i=SF_LEN, pcb1=AdaptCB+ACB_SIZE-SF_LEN,pcb2=AdaptCB+ACB_SIZE-SF_LEN-m; i>0; --i,++pcb1,++pcb2) {
	                *pcb1= *pcb2;
                }
        }
        /*  Fractional update if fractional part isn't zero */
        else {
                if (USE_LONG_INTERP_WINDOWS) { /* if they match */
                        delay_long(&AdaptCB[ACB_SIZE-SF_LEN], frac, m, buf);
                }
                else {
                        delay_short(&AdaptCB[ACB_SIZE-SF_LEN], frac, m, buf);
                }
                for(i=SF_LEN, pcb1= AdaptCB+ACB_SIZE-SF_LEN, pcb2= buf; i>0; --i,++pcb1,++pcb2) {
	                *pcb1= *pcb2;
                }
        }

        /*  Return residual with scaled adaptive CB added to stochastic
         *  contribution
         */

        for(i=SF_LEN,pcb1=AdaptCB+ACB_SIZE-SF_LEN,pcb2=residual; i>0; --i,++pcb1,++pcb2) {
                *pcb1 = fxpt_add16(*pcb2,
                    fxpt_mult16_fix(AdaptGain, *pcb1, 14));
                *pcb2 = *pcb1;
        }
FXPT_POPSTATE();
}
